#include "GraphVisualizer.h"
#include "SimpleGraph.h"
#include <iostream>
#include <fstream>
#include <sstream>
#include <cmath>
#include <cstdlib>

using namespace std;

const double kPi = 3.14159265358979323; 
const double Kattract = 0.001;
const double Krepel= 0.001;

void Welcome() {
	cout << "Welcome to CS106L GraphViz!" << endl;
	cout << "This program uses a force-directed graph layout algorithm" << endl;
  cout << "to render sleek, snazzy pictures of various graphs." << endl;
  cout << endl;
}

/*For taking input one line as string */
string GetLine() {
	string result;
	getline(cin, result);
	return result;
}

/*Integer input function */
int GetInteger() {
	while(true) { // Read input until user enters valid data
		stringstream converter;
		converter << GetLine();
	
		/* Try reading an int, continue if we succeeded. */
		int result;
		if(converter >> result) {
			char remaining;
			
			if(converter >> remaining) // Something's left, input is invalid
				cout << "Unexpected character: " << remaining << endl;
			
			else
				return result;
		} else
			cout << "Please enter an integer." << endl;
		
		cout << "Retry: ";
	}
}


/* Prompting user for graph input file name */
void OpenUserFile(ifstream& input) {
	while(true) {
		cout << "Enter filename: ";
		string filename = GetLine();
		
		input.open(filename.c_str()); // File stream is old function so need to convert string to c format.
		
		if(input.is_open()) 
			return;
		
		cout << "Sorry, I can't find the file " << filename << endl;
		input.clear();
	}
}


/* Loading points into point vector from input file */ 
void LoadPoints(SimpleGraph& graph, ifstream& input){
	input >> graph.numOfNodes;
	graph.nodes.resize(graph.numOfNodes);
	
	Edge edgeForInput;
	graph.numOfEdges=0;
	int i=0;
	while(input>>edgeForInput.start>>edgeForInput.end){
		graph.edges.resize(graph.edges.size()+1);
		graph.edges[i].start=edgeForInput.start;	//Inserting edge in the graph
		graph.edges[i].end=edgeForInput.end;
		graph.numOfEdges++;
		i++;
	}	
	
}

/* Implementing logic to initially position the nodes */
void NodePosition(SimpleGraph& graph){
	for(int i=0;i<graph.numOfNodes;i++){
		graph.nodes[i].x = cos (2.0 * static_cast <double> (i) * kPi / graph.numOfNodes);// 
		graph.nodes[i].y = sin (2.0 * static_cast <double> (i) * kPi / graph.numOfNodes);//
	cout<< 	graph.nodes[i].x <<"  "<<graph.nodes[i].y<<endl;	
	}
	cout<<"    1 "<<endl;
}

/* Implementation of code to read graph from disk */
void InitializeGraph(SimpleGraph& graph){
	ifstream input;
	OpenUserFile(input);
	LoadPoints(graph, input);
	NodePosition(graph);
	
}

/* Computing force */
void ComputeForce(SimpleGraph& graph){
	double Frepel;			   //Repulsive force between Nodes
	double Fattract;		   //Attractive force between Edges
	vector <Node> changeDist;  //Vector to store dispalcement
	
	
	changeDist.reserve(graph.numOfNodes);
	
	for(int i=0;i<graph.numOfNodes;i++){
		changeDist[i].x = 0.0;  // Initializing Dispalcement to 0
		changeDist[i].y = 0.0;	// Initializing Dispalcement to 0
	}
	
	
	/*Calculation Of Repultion due to each node pair */ 
	for(int i=0;i<graph.numOfNodes;i++){ //For each node
		for(int j=i+1;j<graph.numOfNodes;j++){
			if(i!=j){
				double theta = atan2((graph.nodes[j].y - graph.nodes[i].y),( graph.nodes[j].x - graph.nodes[i].x) );
				Frepel = Krepel / sqrt(((graph.nodes[j].y - graph.nodes[i].y)*(graph.nodes[j].y - graph.nodes[i].y)) + ((graph.nodes[j].x - graph.nodes[i].x)*(graph.nodes[j].x - graph.nodes[i].x)) );
			
				changeDist[i].x = changeDist[i].x -Frepel * cos(theta);	//Displacemet due to repulsion 
				changeDist[i].y = changeDist[i].y - Frepel * sin(theta);	//Displacemet due to repulsion 
			 
				changeDist[j].x = changeDist[j].x + Frepel * cos(theta);	//Displacemet due to repulsion 
				changeDist[j].y = changeDist[j].y + Frepel * sin(theta);	//Displacemet due to repulsion 
			}
		}
	}
	
	
	
	/*Calculation Of Attraction between each edges */
	for(int i=0;i<graph.numOfEdges;i++){ //For each edge				
		int FirstNode = graph.edges[i].start; //Start Node of edge  
		int SecondNode = graph.edges[i].end;	 //End Node of edge
		
		cout<<"F node : "<<FirstNode<<" Snode   "<<SecondNode<<endl;
		
		
		Fattract = Kattract * ( ((graph.nodes[SecondNode].y - graph.nodes[FirstNode].y )*(graph.nodes[SecondNode].y - graph.nodes[FirstNode].y)) + ((graph.nodes[SecondNode].x - graph.nodes[FirstNode].x )*(graph.nodes[SecondNode].x - graph.nodes[FirstNode].x )) );
		
		double theta = atan2 ((graph.nodes[SecondNode].y - graph.nodes[FirstNode].y) , (graph.nodes[SecondNode].x - graph.nodes[FirstNode].x) ) ;	
		
		changeDist[FirstNode].x += Fattract * cos(theta);	//Displacemet due to attraction
		changeDist[FirstNode].y += Fattract * sin(theta);	//Displacemet due to attraction 
			 
		changeDist[SecondNode].x -= Fattract * cos(theta);	//Displacemet due to attraction
		changeDist[SecondNode].y -= Fattract * sin(theta);	//Displacemet due to attraction
		
	}
	
	/* Final change in point due to attraction and repultion */
	for(int i=0;i<graph.numOfNodes;i++){ //For each node
		graph.nodes[i].x += changeDist[i].x; changeDist[i].x = 0.0 ;
		graph.nodes[i].y += changeDist[i].y; changeDist[i].y = 0.0 ;
		cout<<graph.nodes[i].x<<"  "<<graph.nodes[i].y<<endl;
	}	
	cout<<endl;
	changeDist.clear();
	//DrawGraph(graph);
}	


int main(){
	SimpleGraph graph;
	InitGraphVisualizer();
	Welcome();
	InitializeGraph(graph);
	while(true){
		ComputeForce(graph);
		DrawGraph(graph);
		
		
	}	
	return 0;
}
